using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

namespace HTCollections.Unsafe
{
    [StructLayout(LayoutKind.Sequential)]
    [DebuggerDisplay("Length = {Length}, IsCreated = {IsCreated}")]
    [DebuggerTypeProxy(typeof(UnsafeArrayDebugView<>))]
    public unsafe struct UnsafeArray<T> : IEnumerable<T>, IDisposable
        where T : unmanaged
    {
        [NativeDisableUnsafePtrRestriction] public T* Ptr;
        public int Length;
        public bool IsCreated;
        public Allocator Allocator;

        public static UnsafeArray<T> Create(int capacity = 4, Allocator allocator = Allocator.Persistent)
        {
            UnsafeArray<T> array = new UnsafeArray<T>(capacity, allocator);
            return array;
        }

        public UnsafeArray(int length = 4, Allocator allocator = Allocator.Persistent)
        {
            Allocator = allocator;
            Ptr = UnsafeUtil.Malloc<T>(length, allocator);
            Length = length;
            IsCreated = true;
        }

        public void Dispose()
        {
            if (!IsCreated) return;
            UnsafeUtil.Free(Ptr, Allocator);
            Ptr = null;
            Length = 0;
            Allocator = Allocator.Invalid;
            IsCreated = false;
        }

        public T this[int index]
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                if (!IsCreated)
                    throw new Exception("UnsafeArray is not created");

                CollectionsUtil.CheckIndexInRange(index, Length);

                return Ptr[index];
            }
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            set
            {
                if (!IsCreated)
                    throw new Exception("UnsafeArray is not created");

                CollectionsUtil.CheckIndexInRange(index, Length);

                Ptr[index] = value;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public ref T GetElementRef(int index)
        {
            if (!IsCreated)
                throw new Exception("UnsafeArray is not created");

            CollectionsUtil.CheckIndexInRange(index, Length);

            return ref Ptr[index];
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public T* GetElementPtr(int index)
        {
            if (!IsCreated)
                throw new Exception("UnsafeArray is not created");

            return Ptr + index;
        }

        /// <summary>
        /// 扩容
        /// </summary>
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void Resize(int newLength)
        {
            if (!IsCreated)
                throw new Exception("UnsafeArray is not created");

            if (newLength == Length) return;
            Ptr = UnsafeUtil.ResizeUnsafeArray(Ptr, Length, newLength, Allocator);
            Length = newLength;
        }

        IEnumerator<T> IEnumerable<T>.GetEnumerator()
        {
            return GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Enumerator GetEnumerator()
        {
            if (!IsCreated)
                throw new Exception("UnsafeArray is not created");
            return new Enumerator(Ptr, Length);
        }

        public struct Enumerator : IEnumerator<T>, IDisposable
        {
            T* mPtr;
            int mIndex;
            int mLength;

            public T Current
            {
                [MethodImpl(MethodImplOptions.AggressiveInlining)]
                get => mPtr[mIndex];
            }

            object IEnumerator.Current => Current;

            public Enumerator(T* ptr, int length)
            {
                mPtr = ptr;
                mIndex = -1;
                mLength = length;
            }

            public bool MoveNext()
            {
                mIndex++;
                return mIndex < mLength;
            }

            public void Reset()
            {
                mIndex = -1;
            }

            public void Dispose()
            {
            }
        }
    }

    sealed class UnsafeArrayDebugView<T> where T : unmanaged
    {
        UnsafeArray<T> mArray;

        public UnsafeArrayDebugView(UnsafeArray<T> array)
        {
            mArray = array;
        }

        public T[] Items
        {
            get
            {
                T[] dst = new T[mArray.Length];
                unsafe
                {
                    UnsafeUtil.CopyArray(mArray.Ptr, 0, dst, 0, mArray.Length);
                }

                return dst;
            }
        }
    }
}